package com.googlecode.stil.playground;

import java.io.IOException;
import java.util.Random;

import org.andengine.audio.sound.Sound;
import org.andengine.audio.sound.SoundFactory;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.handler.IUpdateHandler;
import org.andengine.engine.handler.timer.ITimerCallback;
import org.andengine.engine.handler.timer.TimerHandler;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.SpriteBackground;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.entity.sprite.Sprite;
import org.andengine.entity.text.Text;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.font.Font;
import org.andengine.opengl.font.FontFactory;
import org.andengine.opengl.texture.ITexture;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.andengine.opengl.texture.region.TextureRegion;
import org.andengine.opengl.texture.region.TiledTextureRegion;
import org.andengine.opengl.vbo.VertexBufferObjectManager;

import android.graphics.Color;

public class WhackASpriteActivity extends StilBaseGameActivity {

	private static final int CAMERA_WIDTH = 720;
	private static final int CAMERA_HEIGHT = 480;
	private static final float FONT_SIZE = 48;
	private static final int GAME_TIME = 20;

	private TiledTextureRegion moleTextureRegion;
	private TextureRegion bgTextureRegion;
	private Font plokFont;
	private Sound whackSound;

	private int totalScore;
	private int timeRemaining = GAME_TIME;

	@Override
	public EngineOptions onCreateEngineOptions() {
		Camera camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.LANDSCAPE_SENSOR, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera);
		engineOptions.getAudioOptions().setNeedsSound(true);
		return engineOptions;
	}

	@Override
	public void onCreateResources() {
		BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
		BitmapTextureAtlas bitmapTextureAtlas = new BitmapTextureAtlas(this.getTextureManager(), 1024, 1024, TextureOptions.DEFAULT);
		this.bgTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(bitmapTextureAtlas, this, "mole_bg.png", 0, 0);
		this.moleTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(bitmapTextureAtlas, this, "mole.png", 0, 481, 2, 1);
		bitmapTextureAtlas.load();

		FontFactory.setAssetBasePath("fonts/");
		ITexture plokFontTexture = new BitmapTextureAtlas(this.getTextureManager(), 256, 256, TextureOptions.BILINEAR);
		this.plokFont = FontFactory.createFromAsset(this.getFontManager(), plokFontTexture, this.getAssets(), "Plok.ttf", FONT_SIZE, true, Color.BLACK);
		this.plokFont.load();

		SoundFactory.setAssetBasePath("sfx/");
		try {
			this.whackSound = SoundFactory.createSoundFromAsset(getEngine().getSoundManager(), this, "whack.mp3");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public Scene onCreateScene() {
		final Scene scene = new Scene();
		scene.setBackground(new SpriteBackground(new Sprite(0, 0, bgTextureRegion, getVertexBufferObjectManager())));

		final Text scoreText = new Text(10, 5, plokFont, "score: 0" + totalScore, getVertexBufferObjectManager());
		final Text timerText = new Text(CAMERA_WIDTH - 100, 5, plokFont, "" + GAME_TIME, getVertexBufferObjectManager());

		scene.attachChild(scoreText);
		scene.attachChild(timerText);

		IScoreTracker score = new IScoreTracker() {
			@Override
			public void addScore() {
				totalScore++;
				scoreText.setText("score: " + (totalScore < 10 ? "0" + totalScore : totalScore));
			}
		};
		
		float moleWidth = moleTextureRegion.getWidth();
		float moleHeight = moleTextureRegion.getHeight();

		final Mole mole11 = new Mole(0, 0, moleTextureRegion.deepCopy(), score, getVertexBufferObjectManager());
		final Mole mole12 = new Mole(moleWidth, 0, moleTextureRegion.deepCopy(), score, getVertexBufferObjectManager());
		final Mole mole13 = new Mole(moleWidth * 2, 0, moleTextureRegion.deepCopy(), score, getVertexBufferObjectManager());

		final Mole mole21 = new Mole(0, moleHeight, moleTextureRegion.deepCopy(), score, getVertexBufferObjectManager());
		final Mole mole22 = new Mole(moleWidth, moleHeight, moleTextureRegion.deepCopy(), score, getVertexBufferObjectManager());
		final Mole mole23 = new Mole(moleWidth * 2, moleHeight, moleTextureRegion.deepCopy(), score, getVertexBufferObjectManager());

		scene.registerTouchArea(mole11);
		scene.attachChild(mole11);
		scene.registerTouchArea(mole12);
		scene.attachChild(mole12);
		scene.registerTouchArea(mole13);
		scene.attachChild(mole13);

		scene.registerTouchArea(mole21);
		scene.attachChild(mole21);
		scene.registerTouchArea(mole22);
		scene.attachChild(mole22);
		scene.registerTouchArea(mole23);
		scene.attachChild(mole23);

		getEngine().registerUpdateHandler(new TimerHandler(GAME_TIME, new ITimerCallback() {

			@Override
			public void onTimePassed(TimerHandler pTimerHandler) {
				mole11.stop();
				mole12.stop();
				mole13.stop();
				mole21.stop();
				mole22.stop();
				mole23.stop();
				System.out.println("your score is " + totalScore);
			}
		}));

		getEngine().registerUpdateHandler(new TimerHandler(1, true, new ITimerCallback() {
			@Override
			public void onTimePassed(TimerHandler pTimerHandler) {
				if (timeRemaining > 0) {
					timeRemaining--;
					timerText.setText(timeRemaining < 10 ? "0" + timeRemaining : timeRemaining + "");
					System.out.println("Time : " + timeRemaining);
				}
			}
		}));

		return scene;
	}

	public class Mole extends AnimatedSprite {
		private RandomTimerHandler timeHandler;
		private IScoreTracker score;

		public Mole(float x, float y, TiledTextureRegion tr, IScoreTracker score, VertexBufferObjectManager vertexBufferObjectManager) {
			super(x, y, tr, vertexBufferObjectManager);
			this.score = score;

			registerUpdateHandler(timeHandler = new RandomTimerHandler(0.5f, 5, true) {

				@Override
				public void timePassed() {
					Mole.this.setVisible(!Mole.this.isVisible());
					Mole.this.setCurrentTileIndex(0);
				}
			});
		}

		@Override
		public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float pTouchAreaLocalX, float pTouchAreaLocalY) {
			if (isVisible()) {
				if (pSceneTouchEvent.isActionDown()) {
					whackSound.play();
					Mole.this.setCurrentTileIndex(1);
				}
				if (pSceneTouchEvent.isActionUp()) {
					score.addScore();
					Mole.this.setVisible(false);
					Mole.this.setCurrentTileIndex(0);
				}
				return true;
			} else {
				return false;
			}
		}

		public void stop() {
			timeHandler.setAutoReset(false);
		}

		public void start() {
			timeHandler.setAutoReset(true);
		}

	}

	public interface IScoreTracker {
		public void addScore();
	}

	public abstract class RandomTimerHandler implements IUpdateHandler {
		private Random random = new Random();
		private float timerSeconds;
		private float timerSecondsElapsed;
		private boolean timerCallbackTriggered;
		private boolean autoReset;
		private float minSeconds;
		private float maxSeconds;

		public RandomTimerHandler(float minSeconds, float maxSeconds) {
			this(minSeconds, maxSeconds, false);
		}

		public RandomTimerHandler(float minSeconds, float maxSeconds, final boolean autoReset) {
			this.autoReset = autoReset;
			this.minSeconds = minSeconds;
			this.maxSeconds = maxSeconds;
		}

		public void setAutoReset(final boolean autoReset) {
			this.autoReset = autoReset;
		}

		public abstract void timePassed();

		private void changeTime() {
			timerSeconds = random.nextFloat() * maxSeconds;
			if (timerSeconds < minSeconds) {
				changeTime();
			}
		}

		@Override
		public void onUpdate(final float pSecondsElapsed) {
			if (autoReset) {
				timerSecondsElapsed += pSecondsElapsed;
				while (timerSecondsElapsed >= timerSeconds) {
					timerSecondsElapsed -= timerSeconds;
					timePassed();
					changeTime();
				}
			} else {
				if (!timerCallbackTriggered) {
					timerSecondsElapsed += pSecondsElapsed;
					if (timerSecondsElapsed >= timerSeconds) {
						timerCallbackTriggered = true;
						timePassed();
						changeTime();
					}
				}
			}
		}

		@Override
		public void reset() {
			timerCallbackTriggered = false;
			timerSecondsElapsed = 0;
		}
	}
}